package com.fengwk.support.util;

import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;

import com.fengwk.support.exception.BaseException;
import com.fengwk.support.function.Return;
import com.fengwk.support.math.DerivedFunction;

/**
 * 数学工具
 */
public class MathUtils {
	
	public final static int DEFAULT_DIV_SCALE = 8;
	
	private final static BigDecimal BIG_DECIMAL_ZERO = bigDecimal("0");
	private final static BigDecimal BIG_DECIMAL_ONE = bigDecimal("1");
	
	private MathUtils() {
		throw new BaseException("can not instance " + getClass());
	}
	
	/**
	 * 返回两Integer值之间的随机数
	 */
	public static Integer getRandomBetweenInteger(Integer min, Integer max) {
		return ThreadLocalRandom.current().nextInt(max - min + 1) + min;
	}
	
	/** 
	 * 随机指定范围内N个不重复的数
	 * 
	 * @param min 指定范围最小值 
	 * @param max 指定范围最大值 
	 * @param n 随机数个数 
	 */  
	public static Integer[] countRandom(int min, int max, int count) {
		Set<Integer> iSet = new HashSet<Integer>();
		if(max - min + 1 <= count) {
			for(int i = 1; i <= max; i ++) {
				iSet.add(i);
			}
		} else {
			while(iSet.size() < count) {
				iSet.add(getRandomBetweenInteger(min, max));
			}
		}
		return iSet.toArray(new Integer[iSet.size()]);
	}
	
	/**
	 * 符号相等
	 * 
	 * @param o1
	 * @param o2
	 * @return
	 */
	public static boolean symbolEquals(String m1, String m2) {
		return ObjectUtils.equals(symbolReplace(m1), symbolReplace(m2));
	}
	
	/**
	 * 符号相等
	 * 
	 * @param str
	 * @return
	 */
	private static String symbolReplace(String str) {
		if(str == null) return str;
		return StringUtils.toDbc(str).replaceAll("[+]", "+").replaceAll("[-]", "-").replaceAll("[×|x|*]", "*").replaceAll("[÷|/]", "/").replaceAll("[\\^]", "^");
	}
	
	/**
	 * 正确构建BigDecimal
	 * 
	 * @param num
	 * @return
	 */
	public static BigDecimal bigDecimal(Number num) {
		if (num instanceof Integer)
			return bigDecimal((Integer) num);
		if (num instanceof Long)
			return bigDecimal((Long) num);
		if (num instanceof Double)
			return bigDecimal((Double) num);
		if (num instanceof Float)
			return bigDecimal((Float) num);
		return bigDecimal(num + "");
	}
	
	/**
	 * 正确构建BigDecimal
	 * 
	 * @param i
	 * @return
	 */
	public static BigDecimal bigDecimal(Integer i) {
		return bigDecimal((int) i);
	}
	
	/**
	 * 正确构建BigDecimal
	 * 
	 * @param l
	 * @return
	 */
	public static BigDecimal bigDecimal(Long l) {
		return bigDecimal((long) l);
	}
	
	/**
	 * 正确构建BigDecimal
	 * 
	 * @param f
	 * @return
	 */
	public static BigDecimal bigDecimal(Float f) {
		return bigDecimal((float) f);
	}
	
	/**
	 * 正确构建BigDecimal
	 * 
	 * @param d
	 * @return
	 */
	public static BigDecimal bigDecimal(Double d) {
		return bigDecimal((double) d);
	}
	
	/**
	 * 正确构建BigDecimal
	 * 
	 * @param i
	 * @return
	 */
	public static BigDecimal bigDecimal(int i) {
		return bigDecimal(Integer.toString(i));
	}
	
	/**
	 * 正确构建BigDecimal
	 * 
	 * @param l
	 * @return
	 */
	public static BigDecimal bigDecimal(long l) {
		return bigDecimal(Long.toString(l));
	}
	
	/**
	 * 正确构建BigDecimal
	 * 
	 * @param f
	 * @return
	 */
	public static BigDecimal bigDecimal(float f) {
		return bigDecimal(Float.toString(f));
	}
	
	/**
	 * 正确构建BigDecimal
	 * 
	 * @param d
	 * @return
	 */
	public static BigDecimal bigDecimal(double d) {
		return bigDecimal(Double.toString(d));
	}
	
	/**
	 * 正确构建BigDecimal
	 * 
	 * @param s
	 * @return
	 */
	public static BigDecimal bigDecimal(String s) {
		if(StringUtils.isBlank(s)) {
			return null;
		}
		return new BigDecimal(s);
	}
	
	/**
	 * 比较相等
	 * 
	 * @param b1
	 * @param b2
	 * @return
	 */
	public static boolean equal(BigDecimal b1, BigDecimal b2) {
	    if (b1 == null)
	        throw new NullPointerException();
	    if (b2 == null)
            throw new NullPointerException();
	    return b1.compareTo(b2) == 0;
	}
	
	/**
	 * 提供精确的加法运算
	 * 
	 * @param b1 被加数
	 * @param b2 加数
	 * @return b1 + b2
	 */
	public static BigDecimal add(BigDecimal b1, BigDecimal b2) {
		return b1.add(b2);
	}
	
	/**
	 * 提供精确的加法运算
	 * 
	 * @param d1
	 * @param d2
	 * @return
	 */
	public static double add(double d1, double d2) {
		return add(bigDecimal(d1), bigDecimal(d2)).doubleValue();
	}
	
	/**
	 * 提供精确的加法运算
	 * 
	 * @param f1
	 * @param f2
	 * @return
	 */
	public static float add(float f1, float f2) {
    	return add(bigDecimal(f1), bigDecimal(f2)).floatValue();
    }
	
	/**
	 * 提供精确的减法运算
	 * 
	 * @param b1 被减数
	 * @param b2 减数
	 * @return b1 - b2
	 */
	public static BigDecimal sub(BigDecimal b1, BigDecimal b2) {
		return b1.subtract(b2);
	}
	
	/**
	 * 提供精确的减法运算
	 * 
	 * @param d1
	 * @param d2
	 * @return
	 */
	public static double sub(double d1, double d2) {
        return sub(bigDecimal(d1), bigDecimal(d2)).doubleValue();
    }
	
	/**
	 * 提供精确的减法运算
	 * 
	 * @param f1
	 * @param f2
	 * @return
	 */
	public static float sub(float f1, float f2) {
        return sub(bigDecimal(f1), bigDecimal(f2)).floatValue();
    }
	
	/**
	 * 提供精确的乘法运算
	 * 
	 * @param b1 被除数
	 * @param b2 乘数
	 * @return b1 * b2
	 */
	public static BigDecimal mul(BigDecimal b1, BigDecimal b2) {
		return b1.multiply(b2);
	}
    
	/**
	 * 提供精确的乘法运算
	 * 
	 * @param d1
	 * @param d2
	 * @return
	 */
    public static double mul(double d1, double d2) {
        return mul(bigDecimal(d1), bigDecimal(d2)).doubleValue();
    }
    
    /**
     * 提供精确的乘法运算
     * 
     * @param f1
     * @param f2
     * @return
     */
    public static float mul(float f1, float f2) {
        return mul(bigDecimal(f1), bigDecimal(f2)).floatValue();
    }
	
    /**
     * 提供(相对)精确的除法运算,使用默认舍入
     * 
     * @param b1
     * @param b2
     * @return
     */
    public static BigDecimal div(BigDecimal b1, BigDecimal b2) {
		return div(b1, b2, DEFAULT_DIV_SCALE);
	}
    
    /**
     * 提供精确的除法运算,不使用舍入模式,在无限小数的情况下会抛出异常
     * 
     * @param b1
     * @param b2
     * @return
     */
    public static BigDecimal divIgnoreRound(BigDecimal b1, BigDecimal b2) {
		return b1.divide(b2);
	}
    
    /**
	 * 提供(相对)精确的除法运算,除不尽时四舍五入
	 * 
	 * @param b1 被除数
	 * @param b2 除数
	 * @param scale 表示表示需要精确到小数点以后几位
	 * @return b1 / b2
	 */
	public static BigDecimal div(BigDecimal b1, BigDecimal b2, int scale) {
		return div(b1, b2, scale, BigDecimal.ROUND_HALF_UP);
	}
	
	/**
	 * 提供(相对)精确的除法运算,除不尽时采样自定义的舍入模式
	 * 
	 * @param b1
	 * @param b2
	 * @param scale
	 * @param pattern 舍入模式BigDecimal.ROUND_
	 * @return
	 */
	public static BigDecimal div(BigDecimal b1, BigDecimal b2, int scale, int pattern) {
		if(scale < 0){
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
		return b1.divide(b2, scale, pattern);
	}
	
	/**
	 * 提供(相对)精确的除法运算,使用默认舍入
	 * 
	 * @param d1
	 * @param d2
	 * @param scale
	 * @return
	 */
	public static double div(double d1, double d2){
    	return div(bigDecimal(d1), bigDecimal(d2)).doubleValue();
    }
	
	/**
	 * 提供精确的除法运算,不使用舍入模式,在无限小数的情况下会抛出异常
	 * 
	 * @param d1
	 * @param d2
	 * @return
	 */
	public static double divIgnoreRound(double d1, double d2) {
		return divIgnoreRound(bigDecimal(d1), bigDecimal(d2)).doubleValue();
	}
	
	/**
	 * 提供(相对)精确的除法运算,除不尽时四舍五入
	 * 
	 * @param d1
	 * @param d2
	 * @param scale
	 * @return
	 */
	public static double div(double d1, double d2, int scale){
    	return div(bigDecimal(d1), bigDecimal(d2), scale).doubleValue();
    }
    
	/**
	 * 提供(相对)精确的除法运算,除不尽时采样自定义的舍入模式
	 * 
	 * @param d1
	 * @param d2
	 * @param scale
	 * @param pattern 舍入模式BigDecimal.ROUND_
	 * @return
	 */
    public static double div(double d1, double d2, int scale, int pattern){
    	return div(bigDecimal(d1), bigDecimal(d2), scale, pattern).doubleValue();
    }
    
    /**
	 * 提供(相对)精确的除法运算,使用默认舍入
	 * 
	 * @param d1
	 * @param d2
	 * @param scale
	 * @return
	 */
    public static float div(float f1, float f2) {
		return div(bigDecimal(f1), bigDecimal(f2)).floatValue();
	}
    
    /**
     * 提供精确的除法运算,不使用舍入模式,在无限小数的情况下会抛出异常
     * 
     * @param f1
     * @param f2
     * @return
     */
	public static float divIgnoreRound(float f1, float f2) {
		return divIgnoreRound(bigDecimal(f1), bigDecimal(f2)).floatValue();
	}
    
    /**
     * 提供(相对)精确的除法运算,除不尽时四舍五入
     * 
     * @param f1
     * @param f2
     * @param scale
     * @return
     */
    public static float div(float f1, float f2, int scale){
    	return div(bigDecimal(f1), bigDecimal(f2), scale).floatValue();
    }
    
    /**
     * 提供(相对)精确的除法运算,除不尽时采样自定义的舍入模式
     * 
     * @param f1
     * @param f2
     * @param scale
	 * @param pattern 舍入模式BigDecimal.ROUND_
     * @return
     */
    public static float div(float f1, float f2, int scale, int pattern){
    	return div(bigDecimal(f1), bigDecimal(f2), scale, pattern).floatValue();
    }
    
    /**
     * 精确的除法运算,忽略by zero异常,发生时默认返回0
     * 
     * @param b1
     * @param b2
     * @param scale
     * @return
     */
    public static BigDecimal divIgnoreByZero(BigDecimal b1, BigDecimal b2, int scale) {
    	return divIgnoreByZero(b1, b2, scale, BigDecimal.ROUND_HALF_UP);
	}
    
    /**
     * 精确的除法运算,忽略by zero异常,发生时默认返回0
     * 
     * @param b1
     * @param b2
     * @param scale
     * @param pattern
     * @return
     */
	public static BigDecimal divIgnoreByZero(BigDecimal b1, BigDecimal b2, int scale, int pattern) {
		if (b2.doubleValue() == BIG_DECIMAL_ZERO.doubleValue()) {
    		return BIG_DECIMAL_ZERO;
    	}
		return div(b1, b2, scale, pattern);
	}
    
	/**
	 * 精确的除法运算,忽略by zero异常,发生时默认返回0
	 * 
	 * @param d1
	 * @param d2
	 * @param scale
	 * @return
	 */
    public static double divIgnoreByZero(double d1, double d2, int scale){
    	return divIgnoreByZero(bigDecimal(d1), bigDecimal(d2), scale).doubleValue();
    }
    
    /**
     * 精确的除法运算,忽略by zero异常,发生时默认返回0
     * 
     * @param d1
     * @param d2
     * @param scale
     * @param pattern
     * @return
     */
    public static double divIgnoreByZero(double d1, double d2, int scale, int pattern){
    	return divIgnoreByZero(bigDecimal(d1), bigDecimal(d2), scale, pattern).doubleValue();
    }
    
    /**
     * 精确的除法运算,忽略by zero异常,发生时默认返回0
     * 
     * @param f1
     * @param f2
     * @param scale
     * @return
     */
    public static float divIgnoreByZero(float f1, float f2, int scale){
    	return divIgnoreByZero(bigDecimal(f1), bigDecimal(f2), scale).floatValue();
    }
    
    /**
     * 精确的除法运算,忽略by zero异常,发生时默认返回0
     * 
     * @param f1
     * @param f2
     * @param scale
     * @param pattern
     * @return
     */
    public static float divIgnoreByZero(float f1, float f2, int scale, int pattern){
    	return divIgnoreByZero(bigDecimal(f1), bigDecimal(f2), scale, pattern).floatValue();
    }
    
	/**
	 * 四舍五入处理
	 * 
	 * @param b 需要四舍五入的数字
	 * @param scale 小数点后保留位数
	 * @return 四舍五入后的结果
	 */
	public static BigDecimal round(BigDecimal b, int scale) {
        return round(b, scale, BigDecimal.ROUND_HALF_UP);
    }
	
	/**
	 * 四舍五入处理
	 * 
	 * @param b
	 * @param scale 小数点后保留位数
	 * @param pattern 舍入模式BigDecimal.ROUND_
	 * @return
	 */
	public static BigDecimal round(BigDecimal b, int scale, int pattern) {
        return div(b, BIG_DECIMAL_ONE, scale, pattern);
    }
	
	/**
	 * 四舍五入
	 * 
	 * @param d
	 * @param scale 小数点后保留位数
	 * @return
	 */
	public static double round(double d, int scale){
    	return round(bigDecimal(d), scale).doubleValue();
    }
    
	/**
	 * 四舍五入
	 * 
	 * @param d
	 * @param scale 小数点后保留位数
	 * @param pattern 舍入模式BigDecimal.ROUND_
	 * @return
	 */
    public static double round(double d, int scale, int pattern){
    	return round(bigDecimal(d), scale, pattern).doubleValue();
    }
	
    /**
     * 四舍五入
     * 
     * @param f
     * @param scale 小数点后保留位数
     * @return
     */
    public static float round(float f, int scale){
    	return round(bigDecimal(f), scale).floatValue();
    }
    
    /**
     * 四舍五入
     * 
     * @param f
     * @param scale 小数点后保留位数
	 * @param pattern 舍入模式BigDecimal.ROUND_
     * @return
     */
    public static float round(float f, int scale, int pattern){
    	return round(bigDecimal(f), scale, pattern).floatValue();
    }
    
    /**
     * 幂运算
     * 
     * @param b1
     * @param n
     * @return
     */
    public static BigDecimal pow(BigDecimal b, int n) {
    	return b.pow(n);
    }
    
    /**
     * 幂运算
     * 
     * @param b1
     * @param n
     * @return
     */
    public static double pow(double d, int n) {
    	return pow(bigDecimal(d), n).doubleValue();
    }
    
    /**
     * 幂运算
     * 
     * @param b1
     * @param n
     * @return
     */
    public static float pow(float f, int n) {
    	return pow(bigDecimal(f), n).floatValue();
    }
    
    /**
     * 距离
     * 
     * @param a
     * @param b
     * @return
     */
    public static BigDecimal distance(BigDecimal a, BigDecimal b) {
    	return bigDecimal(Math.abs(sub(a, b).doubleValue()));
    }
    
    /**
     * 距离
     * 
     * @param a
     * @param b
     * @return
     */
    public static double distance(double a, double b) {
    	return Math.abs(sub(a, b));
    }
    
    /**
     * 距离
     * 
     * @param a
     * @param b
     * @return
     */
    public static float distance(float a, float b) {
    	return Math.abs(sub(a, b));
    }
    
    /**
     * 距离
     * 
     * @param a
     * @param b
     * @return
     */
    public static int distance(int a, int b) {
    	return Math.abs(a - b);
    }
    
    /**
     * 距离
     * 
     * @param a
     * @param b
     * @return
     */
    public static long distance(long a, long b) {
    	return Math.abs(a - b);
    }
    
    /**
     * 距离
     * 
     * @param a
     * @param b
     * @return
     */
    public static short distance(short a, short b) {
    	short c = (short) (a - b);
    	if (c < 0)
    		c = (short) -c;
    	return c;
    }
    
    /**
     * 最大值
     * 
     * @param nums
     * @return
     */
    public static Number maxNum(Number...nums) {
        double max = Double.MIN_VALUE;
        for (Number n: nums) {
            double d = n.doubleValue();
            if (d > max)
                max = d;
        }
        return max;
    }
    
    /**
     * 最大值
     * 
     * @param nums
     * @return
     */
    public static int max(int...nums) {
    	int max = nums[0];
    	for(int i = 1; i < nums.length; i ++) {
    		if(nums[i] > max) {
    			max = nums[i];
    		}
    	}
    	return max;
    }
    
    /**
     * 最大值
     * 
     * @param nums
     * @return
     */
    public static long max(long...nums) {
    	long max = nums[0];
    	for(int i = 1; i < nums.length; i ++) {
    		if(nums[i] > max) {
    			max = nums[i];
    		}
    	}
    	return max;
    }
    
    /**
     * 最大值
     * 
     * @param nums
     * @return
     */
    public static float max(float...nums) {
    	float max = nums[0];
    	for(int i = 1; i < nums.length; i ++) {
    		if(nums[i] > max) {
    			max = nums[i];
    		}
    	}
    	return max;
    }
    
    /**
     * 最大值
     * 
     * @param nums
     * @return
     */
    public static double max(double...nums) {
    	double max = nums[0];
    	for(int i = 1; i < nums.length; i ++) {
    		if(nums[i] > max) {
    			max = nums[i];
    		}
    	}
    	return max;
    }
    
    /**
     * 最小值
     * 
     * @param nums
     * @return
     */
    public static Number minNum(Number...nums) {
        double min = Double.MAX_VALUE;
        for (Number n: nums) {
            double d = n.doubleValue();
            if (d < min)
                min = d;
        }
        return min;
    }
    
    /**
     * 最小值
     * 
     * @param nums
     * @return
     */
    public static int min(int...nums) {
    	int max = nums[0];
    	for(int i = 1; i < nums.length; i ++) {
    		if(nums[i] < max) {
    			max = nums[i];
    		}
    	}
    	return max;
    }
    
    /**
     * 最小值
     * 
     * @param nums
     * @return
     */
    public static long min(long...nums) {
    	long max = nums[0];
    	for(int i = 1; i < nums.length; i ++) {
    		if(nums[i] < max) {
    			max = nums[i];
    		}
    	}
    	return max;
    }
    
    /**
     * 最小值
     * 
     * @param nums
     * @return
     */
    public static float min(float...nums) {
    	float max = nums[0];
    	for(int i = 1; i < nums.length; i ++) {
    		if(nums[i] < max) {
    			max = nums[i];
    		}
    	}
    	return max;
    }
    
    /**
     * 最小值
     * 
     * @param nums
     * @return
     */
    public static double min(double...nums) {
    	double max = nums[0];
    	for(int i = 1; i < nums.length; i ++) {
    		if(nums[i] < max) {
    			max = nums[i];
    		}
    	}
    	return max;
    }
    
    /**
     * 获取数字符号位
     * 
     * @param num
     * @return
     */
    public static Number sign(Number num) {
        double d = num.doubleValue();
        if (d > 0)
            return 1;
        else if (d < 0)
            return -1;
        else
            return 0;
    }
    
    /**
     * 求均值
     * 
     * @param num
     * @return
     */
    public static Number mean(Number...nums) {
        return sum(nums).doubleValue() / nums.length;
    }
    
    /**
     * 求和
     * 
     * @param nums
     * @return
     */
    public static Number sum(Number...nums) {
        if (ArrayUtils.isBlank(nums))
            throw new BaseException("ArrayUtils.isBlank(num)");
        double sum = nums[0].doubleValue();
        for (int i = 1; i < nums.length; i ++) 
            sum += nums[i].doubleValue();
        return sum;
    }
    
    /**
     * 比较<br/>
     * num1>num2:1<br/>
     * num1=num2:0<br/>
     * num1<num2:-1
     * 
     * @param num1
     * @param num2
     * @return
     */
    public static Number compare(Number num1, Number num2) {
        double d1 = num1.doubleValue(), d2 = num2.doubleValue();
        return d1 > d2 ? 1 : (d1 == d2 ? 0 : -1);
    }
    
    /**
     * 粗略计算函数求导
     * 
     * @param fun 方程
     * @param x x值
     * @param order 几阶导数
     * @return
     */
    public static BigDecimal derived1(Return.Params1<BigDecimal, BigDecimal> fun, BigDecimal x, int order) {
    	DerivedFunction df = null;
    	for (int i = 0; i < order; i ++) {
    		if (df == null)
    			df = new DerivedFunction(fun);
    		else 
    			df = new DerivedFunction(df);
    	}
    	return df.apply(x);
    }
    
}
